In this notebook, we are interested in exploring the use of miQC as an additional approach to filtering cells to remove any remaining cells that may not be viable or have low sequencing information.

Here, I am particularly comparing the use of miQC to using hard thresholds, in particular removing cells with greater than 20% mitochondrial content, less than 500 genes detected per cell, and less than 1000 total counts per cell.

For the most part, I followed the instructions in the miQC vignette for plotting and filtering.

I’m choosing to compare 2 single-cell samples (SCPCR000126, SCPCR000127) and 2 single-nuclei samples (SCPCR000118, SCPCR000119). We are particularly interested in how miQC will handle single-nuclei samples, as they should not contain many reads corresponding to mitochondrial genes.

Setup

library(magrittr)
library(ggplot2)
library(SingleCellExperiment)
library(scpcaTools)
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:Biobase’:

    combine

The following object is masked from ‘package:BiocGenerics’:

    combine
# file paths to emptyDrops filtered sces from Alevin-fry 
base_dir <- here::here()
cr_like_results_dir <- file.path(base_dir, "data", "results")

cr_like_sce_file <- file.path(cr_like_results_dir, "alevin-fry-cr-like-em-emptydrops-200-sces.rds")

cr_like_sce <- readr::read_rds(cr_like_sce_file)
cannot open file '/Users/allyhawkins/Documents/ALSF/git_repos/scpca-nf/data/results/alevin-fry-cr-like-em-emptydrops-200-sces.rds': No such file or directoryError in readRDS(con, refhook = refhook) : cannot open the connection
# read in mito gene list
sample_info_dir <- file.path(base_dir, "sample-info")
mito_file <- file.path(sample_info_dir, "Homo_sapiens.GRCh38.103.mitogenes.txt")
mito_genes <- readr::read_tsv(mito_file, col_names = "gene_id")
Rows: 37 Columns: 1
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): gene_id

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mito_genes <- mito_genes %>%
  dplyr::pull(gene_id) %>%
  unique()

Plot Metrics

Before we can do any modeling and perform any filtering, let’s look at the distribution of mitochondrial fraction per cell and unique genes detected per cell. The assumptions of miQC rely on having a distribution of mitochondrial reads and unique genes found. It uses a joint model to look at the proportion of reads mapping to mitochondrial DNA and the number of detected genes and determines the probability of a cell being compromised based on this proportion.

To start we have to calculate the per cell statistics using addPerCellQC().

# add per cell qc 
cr_like_sce <- cr_like_sce %>%
  purrr::map(scater::addPerCellQC, subsets = list(mito = mito_genes))
plot_miQC_metrics <- function(sce, sample_name){
  p <- miQC::plotMetrics(sce) +
    ggtitle(sample_name)
  print(p)
}

purrr::iwalk(cr_like_sce, plot_miQC_metrics)

Already you can see that the plot shows a distribution of mitochondrial reads from 0-100% for the two single cell samples, but not for the single-nuclei samples. This could affect the ability of the model to distinguish compromised cells.

Plot Model

Now we can actually look at the miQC model and what cells will be considered compromised and filtered out. Here, I’m plotting the probabilities that each cell is compromised as calculated by miQC, which cells would be filtered out using a given cutoff, and then comparing it to if we were to filter our cells using a pre determind threshold.

# function to test miQC modeling with different parameters on sce objects and show plots
model_sce <- function(sce, title, model_type = "linear", posterior_cutoff = 0.75){
  
  # plot distributions of total counts/cell, genes detected/cell and mito content
  coldata_df <- data.frame(colData(sce))
  manual_filter_plot <- ggplot(coldata_df, aes(x = sum, y = detected, color = subsets_mito_percent)) + 
    geom_point(alpha = 0.5) +
    scale_color_viridis_c() + 
    labs(x = "Total Count",
         y = "Number of Genes Expressed",
         color = "Mitochondrial\nFraction") + 
    geom_vline(xintercept = 500) +
    geom_hline(yintercept = 1000) +
    ggtitle(title) +
    theme_classic()
  
  # create model
  sce_model <- miQC::mixtureModel(sce, model_type)
  
  # plot cells colored by probability of cell being compromised or not
  model_plot <- miQC::plotModel(sce, sce_model) +
    theme_classic()
  
  # plot cells that would be filtered using model vs cells that would be filtered using manual QC cutoffs
  miQC_filter_plot <- miQC::plotFiltering(sce, sce_model, posterior_cutoff) + 
    geom_hline(yintercept = 20) +
    geom_vline(xintercept = 500) + 
    theme_classic()
  
  # arrange plots
  grid.arrange(manual_filter_plot, model_plot, miQC_filter_plot, nrow = 2)
}
purrr::iwalk(cr_like_sce, model_sce)

Using the suggested parameters from miQC we see that it does a fairly good job of keeping cells with low mito content and high number of genes detected for the single-cell samples, except for a few cases in 126 and 127 where cells with low mito and high unique genes are being thrown out. For the single-nuclei samples however, it looks like the model is throwing away a lot more cells than we may want to probably due to the low range in mitochondrial percentages to begin with. Let’s see if we alter some of the parameters used, like the cutoff for filtering, or the model used to compute the probability of compromised cells, and if that impacts the output.

Testing Posterior Probability Cutoffs

miQC will remove any cells that have a high probability of being compromised, by default these are cells with higher than 0.75 posterior probability. We can increase that threshold, which means that more cells will be kept and see if we are able to recover some of the cells that were being thrown out previously that we think should have been included (ie. cells with low mito that are being thrown out).

# change the posterior cutoff for miQC to keep throw away cells with > 0.9 probability of being compromised
purrr::iwalk(cr_like_sce, model_sce, posterior_cutoff = 0.9)

It doesn’t appear that increasing the probability threshold rescues very many more cells with low mito content in SCPCR000126 and SCPCR000127 that are being filtered out.

Testing different mixture models

In addition to altering the threshold used for filtering, we can also test the use of different mixture models to calculate the posterior probability of a cell being compromised. We will test use of both the spline and polynomial modes rather than the default linear model. The polynomial model uses a single polynomial to model the entire datset, while spline uses a piecewise continuous function of many polynomials to model the data set.

# test spline mixture model rather than linear 
purrr::iwalk(cr_like_sce, model_sce, model_type = "spline")

Use of the spline model definitely doesn’t seem ideal with one sample, SCPCR000126, showing the reverse of what we would expect and only keeping cells with low mito content. For the other samples we see a similar trend that we had observed in the linear model.

# test polynomial mixture model rather than linear
purrr::iwalk(cr_like_sce, model_sce, model_type = "polynomial")

For the linear model, we still see that cells with low mito are getting excluded from SCPCR000118 and SCPCR000119, but now see that same trend in SCPCR000220 and SCPCR000221 as well. It appears to model the cells with very high number of unique genes and low mito content as cells to exclude when we would rather keep those.

Filter Cells

Now we can compare filtering using miQC vs. using the pre-determined cutoffs to see how the different filtering effects the overall number of cells that are removed. We can also look at the distribution of each of the metrics used for filtering, genes detected/cell, and mito content/cell to see how filtering affects the population of cells. Here we are using a threshold of 500 genes detected per cell and 10% mitochondrial reads.

# function to filter sce using miQC 
miQC_filter <- function(sce, model_type = "linear", posterior_cutoff = 0.75){
  sce_model <- miQC::mixtureModel(sce, model_type)
  filtered_sce <- miQC::filterCells(sce, sce_model)
}

# filter using manual 
manual_filter <- function(sce, 
                          mito_cutoff = 20, 
                          genes_detected_cutoff = 500) {
  
  # get vector of cells to keep 
  cells_keep <- sce$detected > genes_detected_cutoff & 
    sce$subsets_mito_percent < mito_cutoff
  
  # filter sce
  filtered_sce <- sce[,cells_keep]
}

manual_filtered_sce <- purrr::map(cr_like_sce, manual_filter)
miQC_filtered_sce <- purrr::map(cr_like_sce, miQC_filter)
Removing 618 out of 1612 cells.Removing 1792 out of 6520 cells.Removing 959 out of 15870 cells.Removing 1184 out of 15673 cells.Removing 1537 out of 5032 cells.Removing 1145 out of 5306 cells.
manual_filtered_sce_10 <- purrr::map(cr_like_sce, manual_filter, mito_cutoff = 10)
manual_filtered_sce_mito_only <- purrr::map(cr_like_sce, manual_filter, genes_detected_cutoff = 0)
# function to grab number of cells from sce
get_num_cells <- function(sce){
  num_cells <- dim(sce)[2]
}

manual_cell_num <- manual_filtered_sce %>%
  purrr::map(get_num_cells) %>%
  as.data.frame()
rownames(manual_cell_num) <- c("manual_mito_20")

manual_cell_num_10 <- manual_filtered_sce_10 %>%
  purrr::map(get_num_cells) %>%
  as.data.frame()
rownames(manual_cell_num_10) <- c("manual_mito_10")

manual_cell_num_mito_only <- manual_filtered_sce_mito_only %>%
  purrr::map(get_num_cells) %>%
  as.data.frame()
rownames(manual_cell_num_mito_only) <- c("manual_mito_only")

miQC_cell_num <- miQC_filtered_sce %>%
  purrr::map(get_num_cells) %>%
  as.data.frame()
rownames(miQC_cell_num) <- c("miQC")

pre_filter_cell_num <- cr_like_sce %>%
  purrr::map(get_num_cells) %>%
  as.data.frame()
rownames(pre_filter_cell_num) <- c("pre_filtering")

# create dataframe for plotting with number of cells per sce 
combined_cell_num_df <- dplyr::bind_rows(manual_cell_num,
                                         miQC_cell_num,
                                         manual_cell_num_10,
                                         manual_cell_num_mito_only,
                                         pre_filter_cell_num) %>%
  tibble::rownames_to_column("filtering_method") %>%
  tidyr::pivot_longer(cols = starts_with("SCPCR"),
                      names_to = "sample", 
                      values_to = "number_cells")

It looks like manual filtering (with these specific cutoff combinations) are removing more cells than miQC for single-cell, but more cells are removed in single-nuclei samples with either miQC than manual.

Conclusions

  1. Using the default parameters for miQC with the linear mixture model gives the most consistent and reliable results where the majority of cells with low mito and high unique genes are classified as cells to keep, there are still some caveats.
  2. Cells from single-nuclei samples do not show as high a range of mito content and therefore the mixture model suggests a cutoff that leads to cells with a lower mito content than in single-cell being classified as compromised. This however is slightly expected considering single-nuclei samples should have little to no mito reads.
  3. From these results we decided that we would not actually perform any further filtering ourselves, but rather apply miQC to each sample using the linear mixture model to obtain the posterior probability of a cell being compromised. Then we would create a ccdl_suggests column to identify which cells we would suggest filtering.

Suggested next steps include comparing PCA or UMAP results using the different filtering thresholds to identify if any clusters corresponding to “bad cells” remain. Additionally, we will need to identify if we want to add any additional criteria besides the posterior probability computed by miQC to be included as a “keep” cell in the ccdl_suggests column that will be appended to the colData for every filtered sce object.

Session Info

sessioninfo::session_info()
─ Session info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

─ Packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 package              * version  date       lib source                         
 AnnotationDbi          1.52.0   2020-10-27 [1] Bioconductor                   
 askpass                1.1      2019-01-13 [1] CRAN (R 4.0.2)                 
 assertthat             0.2.1    2019-03-21 [1] CRAN (R 4.0.2)                 
 beachmat               2.6.4    2020-12-20 [1] Bioconductor                   
 beeswarm               0.4.0    2021-06-01 [1] CRAN (R 4.0.2)                 
 Biobase              * 2.50.0   2020-10-27 [1] Bioconductor                   
 BiocFileCache          1.14.0   2020-10-27 [1] Bioconductor                   
 BiocGenerics         * 0.36.1   2021-04-16 [1] Bioconductor                   
 BiocManager            1.30.16  2021-06-15 [1] CRAN (R 4.0.2)                 
 BiocNeighbors          1.8.2    2020-12-07 [1] Bioconductor                   
 BiocParallel           1.24.1   2020-11-06 [1] Bioconductor                   
 BiocSingular           1.6.0    2020-10-27 [1] Bioconductor                   
 biomaRt                2.46.3   2021-02-11 [1] Bioconductor                   
 Biostrings             2.58.0   2020-10-27 [1] Bioconductor                   
 bit                    4.0.4    2020-08-04 [1] CRAN (R 4.0.2)                 
 bit64                  4.0.5    2020-08-30 [1] CRAN (R 4.0.2)                 
 bitops                 1.0-7    2021-04-24 [1] CRAN (R 4.0.2)                 
 blob                   1.2.2    2021-07-23 [1] CRAN (R 4.0.2)                 
 bslib                  0.2.5.1  2021-05-18 [1] CRAN (R 4.0.2)                 
 cachem                 1.0.6    2021-08-19 [1] CRAN (R 4.0.2)                 
 cli                    3.0.1    2021-07-17 [1] CRAN (R 4.0.2)                 
 colorspace             2.0-2    2021-06-24 [1] CRAN (R 4.0.2)                 
 crayon                 1.4.1    2021-02-08 [1] CRAN (R 4.0.2)                 
 curl                   4.3.2    2021-06-23 [1] CRAN (R 4.0.2)                 
 DBI                    1.1.1    2021-01-15 [1] CRAN (R 4.0.2)                 
 dbplyr                 2.1.1    2021-04-06 [1] CRAN (R 4.0.2)                 
 DelayedArray           0.16.3   2021-03-24 [1] Bioconductor                   
 DelayedMatrixStats     1.12.3   2021-02-03 [1] Bioconductor                   
 digest                 0.6.27   2020-10-24 [1] CRAN (R 4.0.2)                 
 dplyr                  1.0.7    2021-06-18 [1] CRAN (R 4.0.2)                 
 dqrng                  0.3.0    2021-05-01 [1] CRAN (R 4.0.2)                 
 DropletUtils           1.13.2   2021-08-30 [1] Bioconductor                   
 edgeR                  3.32.1   2021-01-14 [1] Bioconductor                   
 eisaR                  1.2.0    2020-10-27 [1] Bioconductor                   
 ellipsis               0.3.2    2021-04-29 [1] CRAN (R 4.0.2)                 
 evaluate               0.14     2019-05-28 [1] CRAN (R 4.0.1)                 
 fansi                  0.5.0    2021-05-25 [1] CRAN (R 4.0.2)                 
 farver                 2.1.0    2021-02-28 [1] CRAN (R 4.0.2)                 
 fastmap                1.1.0    2021-01-25 [1] CRAN (R 4.0.2)                 
 flexmix                2.3-17   2020-10-12 [1] CRAN (R 4.0.2)                 
 generics               0.1.0    2020-10-31 [1] CRAN (R 4.0.2)                 
 GenomeInfoDb         * 1.26.7   2021-04-08 [1] Bioconductor                   
 GenomeInfoDbData       1.2.4    2021-04-09 [1] Bioconductor                   
 GenomicAlignments      1.26.0   2020-10-27 [1] Bioconductor                   
 GenomicFeatures        1.42.3   2021-04-04 [1] Bioconductor                   
 GenomicRanges        * 1.42.0   2020-10-27 [1] Bioconductor                   
 getopt                 1.20.3   2019-03-22 [1] CRAN (R 4.0.2)                 
 ggbeeswarm             0.6.0    2017-08-07 [1] CRAN (R 4.0.2)                 
 ggplot2              * 3.3.5    2021-06-25 [1] CRAN (R 4.0.2)                 
 glue                   1.4.2    2020-08-27 [1] CRAN (R 4.0.2)                 
 gridExtra            * 2.3      2017-09-09 [1] CRAN (R 4.0.2)                 
 grr                    0.9.5    2016-08-26 [1] CRAN (R 4.0.2)                 
 gtable                 0.3.0    2019-03-25 [1] CRAN (R 4.0.2)                 
 HDF5Array              1.18.1   2021-02-04 [1] Bioconductor                   
 here                   1.0.1    2020-12-13 [1] CRAN (R 4.0.2)                 
 hms                    1.1.0    2021-05-17 [1] CRAN (R 4.0.2)                 
 htmltools              0.5.2    2021-08-25 [1] CRAN (R 4.0.5)                 
 httr                   1.4.2    2020-07-20 [1] CRAN (R 4.0.2)                 
 IRanges              * 2.24.1   2020-12-12 [1] Bioconductor                   
 irlba                  2.3.3    2019-02-05 [1] CRAN (R 4.0.2)                 
 jquerylib              0.1.4    2021-04-26 [1] CRAN (R 4.0.2)                 
 jsonlite               1.7.2    2020-12-09 [1] CRAN (R 4.0.2)                 
 knitr                  1.33     2021-04-24 [1] CRAN (R 4.0.2)                 
 labeling               0.4.2    2020-10-20 [1] CRAN (R 4.0.2)                 
 lattice                0.20-44  2021-05-02 [1] CRAN (R 4.0.2)                 
 lifecycle              1.0.0    2021-02-15 [1] CRAN (R 4.0.2)                 
 limma                  3.46.0   2020-10-27 [1] Bioconductor                   
 locfit                 1.5-9.4  2020-03-25 [1] CRAN (R 4.0.2)                 
 magrittr             * 2.0.1    2020-11-17 [1] CRAN (R 4.0.2)                 
 Matrix                 1.3-4    2021-06-01 [1] CRAN (R 4.0.2)                 
 Matrix.utils           0.9.8    2020-02-26 [1] CRAN (R 4.0.2)                 
 MatrixGenerics       * 1.2.1    2021-01-30 [1] Bioconductor                   
 matrixStats          * 0.60.1   2021-08-23 [1] CRAN (R 4.0.2)                 
 memoise                2.0.0    2021-01-26 [1] CRAN (R 4.0.2)                 
 miQC                   0.99.9   2021-04-19 [1] Github (greenelab/miQC@1c260dc)
 modeltools             0.2-23   2020-03-05 [1] CRAN (R 4.0.2)                 
 munsell                0.5.0    2018-06-12 [1] CRAN (R 4.0.2)                 
 nnet                   7.3-16   2021-05-03 [1] CRAN (R 4.0.2)                 
 openssl                1.4.4    2021-04-30 [1] CRAN (R 4.0.2)                 
 optparse               1.6.6    2020-04-16 [1] CRAN (R 4.0.2)                 
 pillar                 1.6.2    2021-07-29 [1] CRAN (R 4.0.2)                 
 pkgconfig              2.0.3    2019-09-22 [1] CRAN (R 4.0.2)                 
 prettyunits            1.1.1    2020-01-24 [1] CRAN (R 4.0.2)                 
 progress               1.2.2    2019-05-16 [1] CRAN (R 4.0.2)                 
 purrr                  0.3.4    2020-04-17 [1] CRAN (R 4.0.2)                 
 R.methodsS3            1.8.1    2020-08-26 [1] CRAN (R 4.0.2)                 
 R.oo                   1.24.0   2020-08-26 [1] CRAN (R 4.0.2)                 
 R.utils                2.10.1   2020-08-26 [1] CRAN (R 4.0.2)                 
 R6                     2.5.1    2021-08-19 [1] CRAN (R 4.0.2)                 
 rappdirs               0.3.3    2021-01-31 [1] CRAN (R 4.0.2)                 
 Rcpp                   1.0.7    2021-07-07 [1] CRAN (R 4.0.2)                 
 RCurl                  1.98-1.4 2021-08-17 [1] CRAN (R 4.0.2)                 
 readr                  2.0.1    2021-08-10 [1] CRAN (R 4.0.2)                 
 rhdf5                  2.34.0   2020-10-27 [1] Bioconductor                   
 rhdf5filters           1.2.1    2021-05-03 [1] Bioconductor                   
 Rhdf5lib               1.12.1   2021-01-26 [1] Bioconductor                   
 rlang                  0.4.11   2021-04-30 [1] CRAN (R 4.0.2)                 
 rmarkdown              2.10     2021-08-06 [1] CRAN (R 4.0.2)                 
 rprojroot              2.0.2    2020-11-15 [1] CRAN (R 4.0.2)                 
 Rsamtools              2.6.0    2020-10-27 [1] Bioconductor                   
 RSQLite                2.2.8    2021-08-21 [1] CRAN (R 4.0.2)                 
 rstudioapi             0.13     2020-11-12 [1] CRAN (R 4.0.2)                 
 rsvd                   1.0.5    2021-04-16 [1] CRAN (R 4.0.2)                 
 rtracklayer            1.50.0   2020-10-27 [1] Bioconductor                   
 S4Vectors            * 0.28.1   2020-12-09 [1] Bioconductor                   
 sass                   0.4.0    2021-05-12 [1] CRAN (R 4.0.5)                 
 scales                 1.1.1    2020-05-11 [1] CRAN (R 4.0.2)                 
 scater                 1.18.6   2021-02-26 [1] Bioconductor                   
 scpcaTools           * 0.1.0    2021-09-08 [1] local                          
 scuttle                1.0.4    2020-12-17 [1] Bioconductor                   
 sessioninfo            1.1.1    2018-11-05 [1] CRAN (R 4.0.2)                 
 SingleCellExperiment * 1.12.0   2020-10-27 [1] Bioconductor                   
 sparseMatrixStats      1.2.1    2021-02-02 [1] Bioconductor                   
 stringi                1.7.4    2021-08-25 [1] CRAN (R 4.0.5)                 
 stringr                1.4.0    2019-02-10 [1] CRAN (R 4.0.2)                 
 SummarizedExperiment * 1.20.0   2020-10-27 [1] Bioconductor                   
 tibble                 3.1.4    2021-08-25 [1] CRAN (R 4.0.5)                 
 tidyr                  1.1.3    2021-03-03 [1] CRAN (R 4.0.2)                 
 tidyselect             1.1.1    2021-04-30 [1] CRAN (R 4.0.2)                 
 tinytex                0.33     2021-08-05 [1] CRAN (R 4.0.2)                 
 tzdb                   0.1.2    2021-07-20 [1] CRAN (R 4.0.5)                 
 utf8                   1.2.2    2021-07-24 [1] CRAN (R 4.0.2)                 
 vctrs                  0.3.8    2021-04-29 [1] CRAN (R 4.0.2)                 
 vipor                  0.4.5    2017-03-22 [1] CRAN (R 4.0.2)                 
 viridis                0.6.1    2021-05-11 [1] CRAN (R 4.0.2)                 
 viridisLite            0.4.0    2021-04-13 [1] CRAN (R 4.0.2)                 
 vroom                  1.5.4    2021-08-05 [1] CRAN (R 4.0.2)                 
 withr                  2.4.2    2021-04-18 [1] CRAN (R 4.0.5)                 
 xfun                   0.25     2021-08-06 [1] CRAN (R 4.0.2)                 
 XML                    3.99-0.7 2021-08-17 [1] CRAN (R 4.0.2)                 
 xml2                   1.3.2    2020-04-23 [1] CRAN (R 4.0.2)                 
 XVector                0.30.0   2020-10-28 [1] Bioconductor                   
 yaml                   2.2.1    2020-02-01 [1] CRAN (R 4.0.2)                 
 zlibbioc               1.36.0   2020-10-28 [1] Bioconductor                   

[1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
LS0tCnRpdGxlOiAiTWlRQyBFeHBsb3JhdGlvbiBmb3IgU2NQQ0EiCmF1dGhvcjogIkFsbHkgSGF3a2lucyBmb3IgQ0NETCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBleHBsb3JpbmcgdGhlIHVzZSBvZiBbbWlRQ10oaHR0cHM6Ly9naXRodWIuY29tL2dyZWVuZWxhYi9taVFDKSBhcyBhbiBhZGRpdGlvbmFsIGFwcHJvYWNoIHRvIGZpbHRlcmluZyBjZWxscyB0byByZW1vdmUgYW55IHJlbWFpbmluZyBjZWxscyB0aGF0IG1heSBub3QgYmUgdmlhYmxlIG9yIGhhdmUgbG93IHNlcXVlbmNpbmcgaW5mb3JtYXRpb24uIAoKSGVyZSwgSSBhbSBwYXJ0aWN1bGFybHkgY29tcGFyaW5nIHRoZSB1c2Ugb2YgbWlRQyB0byB1c2luZyBoYXJkIHRocmVzaG9sZHMsIGluIHBhcnRpY3VsYXIgcmVtb3ZpbmcgY2VsbHMgd2l0aCBncmVhdGVyIHRoYW4gMjAlIG1pdG9jaG9uZHJpYWwgY29udGVudCwgbGVzcyB0aGFuIDUwMCBnZW5lcyBkZXRlY3RlZCBwZXIgY2VsbCwgYW5kIGxlc3MgdGhhbiAxMDAwIHRvdGFsIGNvdW50cyBwZXIgY2VsbC4gCgpGb3IgdGhlIG1vc3QgcGFydCwgSSBmb2xsb3dlZCB0aGUgaW5zdHJ1Y3Rpb25zIGluIHRoZSBbbWlRQyB2aWduZXR0ZV0oaHR0cHM6Ly9naXRodWIuY29tL2dyZWVuZWxhYi9taVFDL2Jsb2IvbWFpbi92aWduZXR0ZXMvbWlRQy5SbWQpIGZvciBwbG90dGluZyBhbmQgZmlsdGVyaW5nLiAKCkknbSBjaG9vc2luZyB0byBjb21wYXJlIDIgc2luZ2xlLWNlbGwgc2FtcGxlcyAoU0NQQ1IwMDAxMjYsIFNDUENSMDAwMTI3KSBhbmQgMiBzaW5nbGUtbnVjbGVpIHNhbXBsZXMgKFNDUENSMDAwMTE4LCBTQ1BDUjAwMDExOSkuIFdlIGFyZSBwYXJ0aWN1bGFybHkgaW50ZXJlc3RlZCBpbiBob3cgbWlRQyB3aWxsIGhhbmRsZSBzaW5nbGUtbnVjbGVpIHNhbXBsZXMsIGFzIHRoZXkgc2hvdWxkIG5vdCBjb250YWluIG1hbnkgcmVhZHMgY29ycmVzcG9uZGluZyB0byBtaXRvY2hvbmRyaWFsIGdlbmVzLiAKCiMjIFNldHVwCgpgYGB7cn0KbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KHNjcGNhVG9vbHMpCmxpYnJhcnkoZ3JpZEV4dHJhKQpgYGAKCgpgYGB7cn0KIyBmaWxlIHBhdGhzIHRvIGVtcHR5RHJvcHMgZmlsdGVyZWQgc2NlcyBmcm9tIEFsZXZpbi1mcnkgCmJhc2VfZGlyIDwtIGhlcmU6OmhlcmUoKQpjcl9saWtlX3Jlc3VsdHNfZGlyIDwtIGZpbGUucGF0aChiYXNlX2RpciwgImRhdGEiLCAicmVzdWx0cyIpCgpjcl9saWtlX3NjZV9maWxlIDwtIGZpbGUucGF0aChjcl9saWtlX3Jlc3VsdHNfZGlyLCAiYWxldmluLWZyeS1jci1saWtlLWVtLWVtcHR5ZHJvcHMtMjAwLXNjZXMucmRzIikKCmNyX2xpa2Vfc2NlIDwtIHJlYWRyOjpyZWFkX3Jkcyhjcl9saWtlX3NjZV9maWxlKQpgYGAKCgoKYGBge3J9CiMgcmVhZCBpbiBtaXRvIGdlbmUgbGlzdApzYW1wbGVfaW5mb19kaXIgPC0gZmlsZS5wYXRoKGJhc2VfZGlyLCAic2FtcGxlLWluZm8iKQptaXRvX2ZpbGUgPC0gZmlsZS5wYXRoKHNhbXBsZV9pbmZvX2RpciwgIkhvbW9fc2FwaWVucy5HUkNoMzguMTAzLm1pdG9nZW5lcy50eHQiKQptaXRvX2dlbmVzIDwtIHJlYWRyOjpyZWFkX3RzdihtaXRvX2ZpbGUsIGNvbF9uYW1lcyA9ICJnZW5lX2lkIikKbWl0b19nZW5lcyA8LSBtaXRvX2dlbmVzICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfaWQpICU+JQogIHVuaXF1ZSgpCmBgYAoKCiMjIFBsb3QgTWV0cmljcyAKCkJlZm9yZSB3ZSBjYW4gZG8gYW55IG1vZGVsaW5nIGFuZCBwZXJmb3JtIGFueSBmaWx0ZXJpbmcsIGxldCdzIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtaXRvY2hvbmRyaWFsIGZyYWN0aW9uIHBlciBjZWxsIGFuZCB1bmlxdWUgZ2VuZXMgZGV0ZWN0ZWQgcGVyIGNlbGwuIApUaGUgYXNzdW1wdGlvbnMgb2YgbWlRQyByZWx5IG9uIGhhdmluZyBhIGRpc3RyaWJ1dGlvbiBvZiBtaXRvY2hvbmRyaWFsIHJlYWRzIGFuZCB1bmlxdWUgZ2VuZXMgZm91bmQuIApJdCB1c2VzIGEgam9pbnQgbW9kZWwgdG8gbG9vayBhdCB0aGUgcHJvcG9ydGlvbiBvZiByZWFkcyBtYXBwaW5nIHRvIG1pdG9jaG9uZHJpYWwgRE5BIGFuZCB0aGUgbnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIGFuZCBkZXRlcm1pbmVzIHRoZSBwcm9iYWJpbGl0eSBvZiBhIGNlbGwgYmVpbmcgY29tcHJvbWlzZWQgYmFzZWQgb24gdGhpcyBwcm9wb3J0aW9uLiAKClRvIHN0YXJ0IHdlIGhhdmUgdG8gY2FsY3VsYXRlIHRoZSBwZXIgY2VsbCBzdGF0aXN0aWNzIHVzaW5nIGBhZGRQZXJDZWxsUUMoKWAuIApgYGB7cn0KIyBhZGQgcGVyIGNlbGwgcWMgCmNyX2xpa2Vfc2NlIDwtIGNyX2xpa2Vfc2NlICU+JQogIHB1cnJyOjptYXAoc2NhdGVyOjphZGRQZXJDZWxsUUMsIHN1YnNldHMgPSBsaXN0KG1pdG8gPSBtaXRvX2dlbmVzKSkKYGBgCgpgYGB7cn0KcGxvdF9taVFDX21ldHJpY3MgPC0gZnVuY3Rpb24oc2NlLCBzYW1wbGVfbmFtZSl7CiAgcCA8LSBtaVFDOjpwbG90TWV0cmljcyhzY2UpICsKICAgIGdndGl0bGUoc2FtcGxlX25hbWUpCiAgcHJpbnQocCkKfQoKcHVycnI6Oml3YWxrKGNyX2xpa2Vfc2NlLCBwbG90X21pUUNfbWV0cmljcykKYGBgCkFscmVhZHkgeW91IGNhbiBzZWUgdGhhdCB0aGUgcGxvdCBzaG93cyBhIGRpc3RyaWJ1dGlvbiBvZiBtaXRvY2hvbmRyaWFsIHJlYWRzIGZyb20gMC0xMDAlIGZvciB0aGUgdHdvIHNpbmdsZSBjZWxsIHNhbXBsZXMsIGJ1dCBub3QgZm9yIHRoZSBzaW5nbGUtbnVjbGVpIHNhbXBsZXMuIApUaGlzIGNvdWxkIGFmZmVjdCB0aGUgYWJpbGl0eSBvZiB0aGUgbW9kZWwgdG8gZGlzdGluZ3Vpc2ggY29tcHJvbWlzZWQgY2VsbHMuIAoKIyMgUGxvdCBNb2RlbCAKCk5vdyB3ZSBjYW4gYWN0dWFsbHkgbG9vayBhdCB0aGUgbWlRQyBtb2RlbCBhbmQgd2hhdCBjZWxscyB3aWxsIGJlIGNvbnNpZGVyZWQgY29tcHJvbWlzZWQgYW5kIGZpbHRlcmVkIG91dC4gCkhlcmUsIEknbSBwbG90dGluZyB0aGUgcHJvYmFiaWxpdGllcyB0aGF0IGVhY2ggY2VsbCBpcyBjb21wcm9taXNlZCBhcyBjYWxjdWxhdGVkIGJ5IG1pUUMsIHdoaWNoIGNlbGxzIHdvdWxkIGJlIGZpbHRlcmVkIG91dCB1c2luZyBhIGdpdmVuIGN1dG9mZiwgYW5kIHRoZW4gY29tcGFyaW5nIGl0IHRvIGlmIHdlIHdlcmUgdG8gZmlsdGVyIG91ciBjZWxscyB1c2luZyBhIHByZSBkZXRlcm1pbmQgdGhyZXNob2xkLiAKCmBgYHtyfQojIGZ1bmN0aW9uIHRvIHRlc3QgbWlRQyBtb2RlbGluZyB3aXRoIGRpZmZlcmVudCBwYXJhbWV0ZXJzIG9uIHNjZSBvYmplY3RzIGFuZCBzaG93IHBsb3RzCm1vZGVsX3NjZSA8LSBmdW5jdGlvbihzY2UsIHRpdGxlLCBtb2RlbF90eXBlID0gImxpbmVhciIsIHBvc3Rlcmlvcl9jdXRvZmYgPSAwLjc1KXsKICAKICAjIHBsb3QgZGlzdHJpYnV0aW9ucyBvZiB0b3RhbCBjb3VudHMvY2VsbCwgZ2VuZXMgZGV0ZWN0ZWQvY2VsbCBhbmQgbWl0byBjb250ZW50CiAgY29sZGF0YV9kZiA8LSBkYXRhLmZyYW1lKGNvbERhdGEoc2NlKSkKICBtYW51YWxfZmlsdGVyX3Bsb3QgPC0gZ2dwbG90KGNvbGRhdGFfZGYsIGFlcyh4ID0gc3VtLCB5ID0gZGV0ZWN0ZWQsIGNvbG9yID0gc3Vic2V0c19taXRvX3BlcmNlbnQpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKwogICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKyAKICAgIGxhYnMoeCA9ICJUb3RhbCBDb3VudCIsCiAgICAgICAgIHkgPSAiTnVtYmVyIG9mIEdlbmVzIEV4cHJlc3NlZCIsCiAgICAgICAgIGNvbG9yID0gIk1pdG9jaG9uZHJpYWxcbkZyYWN0aW9uIikgKyAKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDUwMCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTAwMCkgKwogICAgZ2d0aXRsZSh0aXRsZSkgKwogICAgdGhlbWVfY2xhc3NpYygpCiAgCiAgIyBjcmVhdGUgbW9kZWwKICBzY2VfbW9kZWwgPC0gbWlRQzo6bWl4dHVyZU1vZGVsKHNjZSwgbW9kZWxfdHlwZSkKICAKICAjIHBsb3QgY2VsbHMgY29sb3JlZCBieSBwcm9iYWJpbGl0eSBvZiBjZWxsIGJlaW5nIGNvbXByb21pc2VkIG9yIG5vdAogIG1vZGVsX3Bsb3QgPC0gbWlRQzo6cGxvdE1vZGVsKHNjZSwgc2NlX21vZGVsKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKICAKICAjIHBsb3QgY2VsbHMgdGhhdCB3b3VsZCBiZSBmaWx0ZXJlZCB1c2luZyBtb2RlbCB2cyBjZWxscyB0aGF0IHdvdWxkIGJlIGZpbHRlcmVkIHVzaW5nIG1hbnVhbCBRQyBjdXRvZmZzCiAgbWlRQ19maWx0ZXJfcGxvdCA8LSBtaVFDOjpwbG90RmlsdGVyaW5nKHNjZSwgc2NlX21vZGVsLCBwb3N0ZXJpb3JfY3V0b2ZmKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMjApICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDUwMCkgKyAKICAgIHRoZW1lX2NsYXNzaWMoKQogIAogICMgYXJyYW5nZSBwbG90cwogIGdyaWQuYXJyYW5nZShtYW51YWxfZmlsdGVyX3Bsb3QsIG1vZGVsX3Bsb3QsIG1pUUNfZmlsdGVyX3Bsb3QsIG5yb3cgPSAyKQp9CmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQpwdXJycjo6aXdhbGsoY3JfbGlrZV9zY2UsIG1vZGVsX3NjZSkKYGBgClVzaW5nIHRoZSBzdWdnZXN0ZWQgcGFyYW1ldGVycyBmcm9tIG1pUUMgd2Ugc2VlIHRoYXQgaXQgZG9lcyBhIGZhaXJseSBnb29kIGpvYiBvZiBrZWVwaW5nIGNlbGxzIHdpdGggbG93IG1pdG8gY29udGVudCBhbmQgaGlnaCBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgZm9yIHRoZSBzaW5nbGUtY2VsbCBzYW1wbGVzLCBleGNlcHQgZm9yIGEgZmV3IGNhc2VzIGluIDEyNiBhbmQgMTI3IHdoZXJlIGNlbGxzIHdpdGggbG93IG1pdG8gYW5kIGhpZ2ggdW5pcXVlIGdlbmVzIGFyZSBiZWluZyB0aHJvd24gb3V0LgpGb3IgdGhlIHNpbmdsZS1udWNsZWkgc2FtcGxlcyBob3dldmVyLCBpdCBsb29rcyBsaWtlIHRoZSBtb2RlbCBpcyB0aHJvd2luZyBhd2F5IGEgbG90IG1vcmUgY2VsbHMgdGhhbiB3ZSBtYXkgd2FudCB0byBwcm9iYWJseSBkdWUgdG8gdGhlIGxvdyByYW5nZSBpbiBtaXRvY2hvbmRyaWFsIHBlcmNlbnRhZ2VzIHRvIGJlZ2luIHdpdGguIApMZXQncyBzZWUgaWYgd2UgYWx0ZXIgc29tZSBvZiB0aGUgcGFyYW1ldGVycyB1c2VkLCBsaWtlIHRoZSBjdXRvZmYgZm9yIGZpbHRlcmluZywgb3IgdGhlIG1vZGVsIHVzZWQgdG8gY29tcHV0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgY29tcHJvbWlzZWQgY2VsbHMsIGFuZCBpZiB0aGF0IGltcGFjdHMgdGhlIG91dHB1dC4gCgojIyMgVGVzdGluZyBQb3N0ZXJpb3IgUHJvYmFiaWxpdHkgQ3V0b2ZmcwoKbWlRQyB3aWxsIHJlbW92ZSBhbnkgY2VsbHMgdGhhdCBoYXZlIGEgaGlnaCBwcm9iYWJpbGl0eSBvZiBiZWluZyBjb21wcm9taXNlZCwgYnkgZGVmYXVsdCB0aGVzZSBhcmUgY2VsbHMgd2l0aCBoaWdoZXIgdGhhbiAwLjc1IHBvc3RlcmlvciBwcm9iYWJpbGl0eS4gCldlIGNhbiBpbmNyZWFzZSB0aGF0IHRocmVzaG9sZCwgd2hpY2ggbWVhbnMgdGhhdCBtb3JlIGNlbGxzIHdpbGwgYmUga2VwdCBhbmQgc2VlIGlmIHdlIGFyZSBhYmxlIHRvIHJlY292ZXIgc29tZSBvZiB0aGUgY2VsbHMgdGhhdCB3ZXJlIGJlaW5nIHRocm93biBvdXQgcHJldmlvdXNseSB0aGF0IHdlIHRoaW5rIHNob3VsZCBoYXZlIGJlZW4gaW5jbHVkZWQgKGllLiBjZWxscyB3aXRoIGxvdyBtaXRvIHRoYXQgYXJlIGJlaW5nIHRocm93biBvdXQpLiAKCmBgYHtyfQojIGNoYW5nZSB0aGUgcG9zdGVyaW9yIGN1dG9mZiBmb3IgbWlRQyB0byBrZWVwIHRocm93IGF3YXkgY2VsbHMgd2l0aCA+IDAuOSBwcm9iYWJpbGl0eSBvZiBiZWluZyBjb21wcm9taXNlZApwdXJycjo6aXdhbGsoY3JfbGlrZV9zY2UsIG1vZGVsX3NjZSwgcG9zdGVyaW9yX2N1dG9mZiA9IDAuOSkKYGBgCkl0IGRvZXNuJ3QgYXBwZWFyIHRoYXQgaW5jcmVhc2luZyB0aGUgcHJvYmFiaWxpdHkgdGhyZXNob2xkIHJlc2N1ZXMgdmVyeSBtYW55IG1vcmUgY2VsbHMgd2l0aCBsb3cgbWl0byBjb250ZW50IGluIFNDUENSMDAwMTI2IGFuZCBTQ1BDUjAwMDEyNyB0aGF0IGFyZSBiZWluZyBmaWx0ZXJlZCBvdXQuIAoKIyMjIFRlc3RpbmcgZGlmZmVyZW50IG1peHR1cmUgbW9kZWxzIAoKSW4gYWRkaXRpb24gdG8gYWx0ZXJpbmcgdGhlIHRocmVzaG9sZCB1c2VkIGZvciBmaWx0ZXJpbmcsIHdlIGNhbiBhbHNvIHRlc3QgdGhlIHVzZSBvZiBkaWZmZXJlbnQgbWl4dHVyZSBtb2RlbHMgdG8gY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgYSBjZWxsIGJlaW5nIGNvbXByb21pc2VkLiAKV2Ugd2lsbCB0ZXN0IHVzZSBvZiBib3RoIHRoZSBzcGxpbmUgYW5kIHBvbHlub21pYWwgbW9kZXMgcmF0aGVyIHRoYW4gdGhlIGRlZmF1bHQgbGluZWFyIG1vZGVsLiAKVGhlIHBvbHlub21pYWwgbW9kZWwgdXNlcyBhIHNpbmdsZSBwb2x5bm9taWFsIHRvIG1vZGVsIHRoZSBlbnRpcmUgZGF0c2V0LCB3aGlsZSBzcGxpbmUgdXNlcyBhIHBpZWNld2lzZSBjb250aW51b3VzIGZ1bmN0aW9uIG9mIG1hbnkgcG9seW5vbWlhbHMgdG8gbW9kZWwgdGhlIGRhdGEgc2V0LiAKCmBgYHtyfQojIHRlc3Qgc3BsaW5lIG1peHR1cmUgbW9kZWwgcmF0aGVyIHRoYW4gbGluZWFyIApwdXJycjo6aXdhbGsoY3JfbGlrZV9zY2UsIG1vZGVsX3NjZSwgbW9kZWxfdHlwZSA9ICJzcGxpbmUiKQpgYGAKClVzZSBvZiB0aGUgc3BsaW5lIG1vZGVsIGRlZmluaXRlbHkgZG9lc24ndCBzZWVtIGlkZWFsIHdpdGggb25lIHNhbXBsZSwgU0NQQ1IwMDAxMjYsIHNob3dpbmcgdGhlIHJldmVyc2Ugb2Ygd2hhdCB3ZSB3b3VsZCBleHBlY3QgYW5kIG9ubHkga2VlcGluZyBjZWxscyB3aXRoIGxvdyBtaXRvIGNvbnRlbnQuIApGb3IgdGhlIG90aGVyIHNhbXBsZXMgd2Ugc2VlIGEgc2ltaWxhciB0cmVuZCB0aGF0IHdlIGhhZCBvYnNlcnZlZCBpbiB0aGUgbGluZWFyIG1vZGVsLiAKCmBgYHtyfQojIHRlc3QgcG9seW5vbWlhbCBtaXh0dXJlIG1vZGVsIHJhdGhlciB0aGFuIGxpbmVhcgpwdXJycjo6aXdhbGsoY3JfbGlrZV9zY2UsIG1vZGVsX3NjZSwgbW9kZWxfdHlwZSA9ICJwb2x5bm9taWFsIikKYGBgCgpGb3IgdGhlIGxpbmVhciBtb2RlbCwgd2Ugc3RpbGwgc2VlIHRoYXQgY2VsbHMgd2l0aCBsb3cgbWl0byBhcmUgZ2V0dGluZyBleGNsdWRlZCBmcm9tIFNDUENSMDAwMTE4IGFuZCBTQ1BDUjAwMDExOSwgYnV0IG5vdyBzZWUgdGhhdCBzYW1lIHRyZW5kIGluIFNDUENSMDAwMjIwIGFuZCBTQ1BDUjAwMDIyMSBhcyB3ZWxsLiAKSXQgYXBwZWFycyB0byBtb2RlbCB0aGUgY2VsbHMgd2l0aCB2ZXJ5IGhpZ2ggbnVtYmVyIG9mIHVuaXF1ZSBnZW5lcyBhbmQgbG93IG1pdG8gY29udGVudCBhcyBjZWxscyB0byBleGNsdWRlIHdoZW4gd2Ugd291bGQgcmF0aGVyIGtlZXAgdGhvc2UuIAoKIyMgRmlsdGVyIENlbGxzCgpOb3cgd2UgY2FuIGNvbXBhcmUgZmlsdGVyaW5nIHVzaW5nIG1pUUMgdnMuIHVzaW5nIHRoZSBwcmUtZGV0ZXJtaW5lZCBjdXRvZmZzIHRvIHNlZSBob3cgdGhlIGRpZmZlcmVudCBmaWx0ZXJpbmcgZWZmZWN0cyB0aGUgb3ZlcmFsbCBudW1iZXIgb2YgY2VsbHMgdGhhdCBhcmUgcmVtb3ZlZC4gCldlIGNhbiBhbHNvIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBlYWNoIG9mIHRoZSBtZXRyaWNzIHVzZWQgZm9yIGZpbHRlcmluZywgZ2VuZXMgZGV0ZWN0ZWQvY2VsbCwgYW5kIG1pdG8gY29udGVudC9jZWxsIHRvIHNlZSBob3cgZmlsdGVyaW5nIGFmZmVjdHMgdGhlIHBvcHVsYXRpb24gb2YgY2VsbHMuIApIZXJlIHdlIGFyZSB1c2luZyBhIHRocmVzaG9sZCBvZiA1MDAgZ2VuZXMgZGV0ZWN0ZWQgcGVyIGNlbGwgYW5kIDEwJSBtaXRvY2hvbmRyaWFsIHJlYWRzLgoKYGBge3J9CiMgZnVuY3Rpb24gdG8gZmlsdGVyIHNjZSB1c2luZyBtaVFDIAptaVFDX2ZpbHRlciA8LSBmdW5jdGlvbihzY2UsIG1vZGVsX3R5cGUgPSAibGluZWFyIiwgcG9zdGVyaW9yX2N1dG9mZiA9IDAuNzUpewogIHNjZV9tb2RlbCA8LSBtaVFDOjptaXh0dXJlTW9kZWwoc2NlLCBtb2RlbF90eXBlKQogIGZpbHRlcmVkX3NjZSA8LSBtaVFDOjpmaWx0ZXJDZWxscyhzY2UsIHNjZV9tb2RlbCkKfQoKIyBmaWx0ZXIgdXNpbmcgbWFudWFsIAptYW51YWxfZmlsdGVyIDwtIGZ1bmN0aW9uKHNjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWl0b19jdXRvZmYgPSAyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXNfZGV0ZWN0ZWRfY3V0b2ZmID0gNTAwKSB7CiAgCiAgIyBnZXQgdmVjdG9yIG9mIGNlbGxzIHRvIGtlZXAgCiAgY2VsbHNfa2VlcCA8LSBzY2UkZGV0ZWN0ZWQgPiBnZW5lc19kZXRlY3RlZF9jdXRvZmYgJiAKICAgIHNjZSRzdWJzZXRzX21pdG9fcGVyY2VudCA8IG1pdG9fY3V0b2ZmCiAgCiAgIyBmaWx0ZXIgc2NlCiAgZmlsdGVyZWRfc2NlIDwtIHNjZVssY2VsbHNfa2VlcF0KfQoKbWFudWFsX2ZpbHRlcmVkX3NjZSA8LSBwdXJycjo6bWFwKGNyX2xpa2Vfc2NlLCBtYW51YWxfZmlsdGVyKQptaVFDX2ZpbHRlcmVkX3NjZSA8LSBwdXJycjo6bWFwKGNyX2xpa2Vfc2NlLCBtaVFDX2ZpbHRlcikKbWFudWFsX2ZpbHRlcmVkX3NjZV8xMCA8LSBwdXJycjo6bWFwKGNyX2xpa2Vfc2NlLCBtYW51YWxfZmlsdGVyLCBtaXRvX2N1dG9mZiA9IDEwKQptYW51YWxfZmlsdGVyZWRfc2NlX21pdG9fb25seSA8LSBwdXJycjo6bWFwKGNyX2xpa2Vfc2NlLCBtYW51YWxfZmlsdGVyLCBnZW5lc19kZXRlY3RlZF9jdXRvZmYgPSAwKQpgYGAKCgpgYGB7cn0KIyBmdW5jdGlvbiB0byBncmFiIG51bWJlciBvZiBjZWxscyBmcm9tIHNjZQpnZXRfbnVtX2NlbGxzIDwtIGZ1bmN0aW9uKHNjZSl7CiAgbnVtX2NlbGxzIDwtIGRpbShzY2UpWzJdCn0KCm1hbnVhbF9jZWxsX251bSA8LSBtYW51YWxfZmlsdGVyZWRfc2NlICU+JQogIHB1cnJyOjptYXAoZ2V0X251bV9jZWxscykgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCnJvd25hbWVzKG1hbnVhbF9jZWxsX251bSkgPC0gYygibWFudWFsX21pdG9fMjAiKQoKbWFudWFsX2NlbGxfbnVtXzEwIDwtIG1hbnVhbF9maWx0ZXJlZF9zY2VfMTAgJT4lCiAgcHVycnI6Om1hcChnZXRfbnVtX2NlbGxzKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKcm93bmFtZXMobWFudWFsX2NlbGxfbnVtXzEwKSA8LSBjKCJtYW51YWxfbWl0b18xMCIpCgptYW51YWxfY2VsbF9udW1fbWl0b19vbmx5IDwtIG1hbnVhbF9maWx0ZXJlZF9zY2VfbWl0b19vbmx5ICU+JQogIHB1cnJyOjptYXAoZ2V0X251bV9jZWxscykgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCnJvd25hbWVzKG1hbnVhbF9jZWxsX251bV9taXRvX29ubHkpIDwtIGMoIm1hbnVhbF9taXRvX29ubHkiKQoKbWlRQ19jZWxsX251bSA8LSBtaVFDX2ZpbHRlcmVkX3NjZSAlPiUKICBwdXJycjo6bWFwKGdldF9udW1fY2VsbHMpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpyb3duYW1lcyhtaVFDX2NlbGxfbnVtKSA8LSBjKCJtaVFDIikKCnByZV9maWx0ZXJfY2VsbF9udW0gPC0gY3JfbGlrZV9zY2UgJT4lCiAgcHVycnI6Om1hcChnZXRfbnVtX2NlbGxzKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKcm93bmFtZXMocHJlX2ZpbHRlcl9jZWxsX251bSkgPC0gYygicHJlX2ZpbHRlcmluZyIpCgojIGNyZWF0ZSBkYXRhZnJhbWUgZm9yIHBsb3R0aW5nIHdpdGggbnVtYmVyIG9mIGNlbGxzIHBlciBzY2UgCmNvbWJpbmVkX2NlbGxfbnVtX2RmIDwtIGRwbHlyOjpiaW5kX3Jvd3MobWFudWFsX2NlbGxfbnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pUUNfY2VsbF9udW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFudWFsX2NlbGxfbnVtXzEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hbnVhbF9jZWxsX251bV9taXRvX29ubHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlX2ZpbHRlcl9jZWxsX251bSkgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImZpbHRlcmluZ19tZXRob2QiKSAlPiUKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiU0NQQ1IiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInNhbXBsZSIsIAogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIm51bWJlcl9jZWxscyIpCmBgYAoKYGBge3J9CmdncGxvdChjb21iaW5lZF9jZWxsX251bV9kZiwgYWVzKHggPSBzYW1wbGUsIHkgPSBudW1iZXJfY2VsbHMsIGZpbGwgPSBmaWx0ZXJpbmdfbWV0aG9kKSkgKyAKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgoKSXQgbG9va3MgbGlrZSBtYW51YWwgZmlsdGVyaW5nICh3aXRoIHRoZXNlIHNwZWNpZmljIGN1dG9mZiBjb21iaW5hdGlvbnMpIGFyZSByZW1vdmluZyBtb3JlIGNlbGxzIHRoYW4gbWlRQyBmb3Igc2luZ2xlLWNlbGwsIGJ1dCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkIGluIHNpbmdsZS1udWNsZWkgc2FtcGxlcyB3aXRoIGVpdGhlciBtaVFDIHRoYW4gbWFudWFsLiAKCgojIyBDb25jbHVzaW9ucwoKMS4gVXNpbmcgdGhlIGRlZmF1bHQgcGFyYW1ldGVycyBmb3IgbWlRQyB3aXRoIHRoZSBsaW5lYXIgbWl4dHVyZSBtb2RlbCBnaXZlcyB0aGUgbW9zdCBjb25zaXN0ZW50IGFuZCByZWxpYWJsZSByZXN1bHRzIHdoZXJlIHRoZSBtYWpvcml0eSBvZiBjZWxscyB3aXRoIGxvdyBtaXRvIGFuZCBoaWdoIHVuaXF1ZSBnZW5lcyBhcmUgY2xhc3NpZmllZCBhcyBjZWxscyB0byBrZWVwLCB0aGVyZSBhcmUgc3RpbGwgc29tZSBjYXZlYXRzLgoyLiBDZWxscyBmcm9tIHNpbmdsZS1udWNsZWkgc2FtcGxlcyBkbyBub3Qgc2hvdyBhcyBoaWdoIGEgcmFuZ2Ugb2YgbWl0byBjb250ZW50IGFuZCB0aGVyZWZvcmUgdGhlIG1peHR1cmUgbW9kZWwgc3VnZ2VzdHMgYSBjdXRvZmYgdGhhdCBsZWFkcyB0byBjZWxscyB3aXRoIGEgbG93ZXIgbWl0byBjb250ZW50IHRoYW4gaW4gc2luZ2xlLWNlbGwgYmVpbmcgY2xhc3NpZmllZCBhcyBjb21wcm9taXNlZC4gVGhpcyBob3dldmVyIGlzIHNsaWdodGx5IGV4cGVjdGVkIGNvbnNpZGVyaW5nIHNpbmdsZS1udWNsZWkgc2FtcGxlcyBzaG91bGQgaGF2ZSBsaXR0bGUgdG8gbm8gbWl0byByZWFkcy4KMy4gRnJvbSB0aGVzZSByZXN1bHRzIHdlIGRlY2lkZWQgdGhhdCB3ZSB3b3VsZCBub3QgYWN0dWFsbHkgcGVyZm9ybSBhbnkgZnVydGhlciBmaWx0ZXJpbmcgb3Vyc2VsdmVzLCBidXQgcmF0aGVyIGFwcGx5IG1pUUMgdG8gZWFjaCBzYW1wbGUgdXNpbmcgdGhlIGxpbmVhciBtaXh0dXJlIG1vZGVsIHRvIG9idGFpbiB0aGUgcG9zdGVyaW9yIHByb2JhYmlsaXR5IG9mIGEgY2VsbCBiZWluZyBjb21wcm9taXNlZC4gVGhlbiB3ZSB3b3VsZCBjcmVhdGUgYSBjY2RsX3N1Z2dlc3RzIGNvbHVtbiB0byBpZGVudGlmeSB3aGljaCBjZWxscyB3ZSB3b3VsZCBzdWdnZXN0IGZpbHRlcmluZy4KClN1Z2dlc3RlZCBuZXh0IHN0ZXBzIGluY2x1ZGUgY29tcGFyaW5nIFBDQSBvciBVTUFQIHJlc3VsdHMgdXNpbmcgdGhlIGRpZmZlcmVudCBmaWx0ZXJpbmcgdGhyZXNob2xkcyB0byBpZGVudGlmeSBpZiBhbnkgY2x1c3RlcnMgY29ycmVzcG9uZGluZyB0byAiYmFkIGNlbGxzIiByZW1haW4uIApBZGRpdGlvbmFsbHksIHdlIHdpbGwgbmVlZCB0byBpZGVudGlmeSBpZiB3ZSB3YW50IHRvIGFkZCBhbnkgYWRkaXRpb25hbCBjcml0ZXJpYSBiZXNpZGVzIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgY29tcHV0ZWQgYnkgbWlRQyB0byBiZSBpbmNsdWRlZCBhcyBhICJrZWVwIiBjZWxsIGluIHRoZSBgY2NkbF9zdWdnZXN0c2AgY29sdW1uIHRoYXQgd2lsbCBiZSBhcHBlbmRlZCB0byB0aGUgYGNvbERhdGFgIGZvciBldmVyeSBmaWx0ZXJlZCBzY2Ugb2JqZWN0LiAKCiMjIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbmluZm86OnNlc3Npb25faW5mbygpCmBgYAoK